#pragma once
#include "Runtime/CGI/CGICommandList.h"
#include "Runtime/Rendering/MeshBatch.h"
#include "Runtime/Rendering/Scene.h"
#include "Runtime/Thread/Task.h"


namespace Alice
{
    class ViewInfo;
    class SceneRenderer;
    class FVisibilityViewPacket;
    class FVisibleMeshDrawCommand;
    class FViewCommands
    {
    public:
        FViewCommands()
        {
            for (int32 PassIndex = 0; PassIndex < EMeshPass::Num; ++PassIndex)
            {
                NumDynamicMeshCommandBuildRequestElements[PassIndex] = 0;
            }
        }
        std::vector<FVisibleMeshDrawCommand*> MeshCommands[EMeshPass::Num];
        std::array<int32,EMeshPass::Num> NumDynamicMeshCommandBuildRequestElements;
        //TStaticArray<FMeshCommandOneFrameArray, EMeshPass::Num> MeshCommands;
        //TStaticArray<int32, EMeshPass::Num> NumDynamicMeshCommandBuildRequestElements;
        //TStaticArray<TArray<const FStaticMeshBatch*, SceneRenderingAllocator>, EMeshPass::Num> DynamicMeshCommandBuildRequests;
        //TStaticArray<TArray<EMeshDrawCommandCullingPayloadFlags, SceneRenderingAllocator>, EMeshPass::Num> DynamicMeshCommandBuildFlags;
    };
    class IVisibilityTaskData
    {
    public:
        /** Processes all visibility tasks that must be performed on the render thread. */
        virtual void ProcessRenderThreadTasks() = 0;
        /** Called to finish processing of the GDME tasks. */
        virtual void FinishGatherDynamicMeshElements(
            //FExclusiveDepthStencil::Type BasePassDepthStencilAccess,
            //FInstanceCullingManager& InstanceCullingManager,
            //FVirtualTextureUpdater* VirtualTextureUpdater
            ) = 0;
        /** Waits for the task graph and cleans up. */
        virtual void Finish() = 0;
    };
    class FVisibilityTaskData : public IVisibilityTaskData
    {
    public:
        CGICommandListImmediate* mCGICommandList;
        Scene* mScene;
        SceneRenderer* mSceneRenderer;
        std::vector<ViewInfo*>& mViews;
        std::vector<FVisibilityViewPacket*> mViewPackets;
        struct FDynamicMeshElements
        {
            std::vector<FViewCommands*> mViewCommandsPerView;
        } mDynamicMeshElements;
        FVisibilityTaskData(CGICommandListImmediate*inCGICommandList,SceneRenderer*inSceneRenderer);
        /*struct FTasks
        {
            // These legacy tasks are used to interface with the jobs launched prior to gather dynamic mesh elements.
            FGraphEventRef FrustumCullLegacyTask;
            FGraphEventRef ComputeRelevanceLegacyTask;

            FGraphEventRef DynamicMeshElementsPipe;
            FGraphEventRef DynamicMeshElementsRenderThread;

            UE::Tasks::FTaskEvent LightVisibility{ UE_SOURCE_LOCATION };
            UE::Tasks::FTaskEvent BeginInitVisibility{ UE_SOURCE_LOCATION };
            UE::Tasks::FTaskEvent FrustumCull{ UE_SOURCE_LOCATION };
            UE::Tasks::FTaskEvent OcclusionCull{ UE_SOURCE_LOCATION };
            UE::Tasks::FTaskEvent ComputeRelevance{ UE_SOURCE_LOCATION };
            UE::Tasks::FTaskEvent DynamicMeshElementsPrerequisites{ UE_SOURCE_LOCATION };
            UE::Tasks::FTaskEvent DynamicMeshElements{ UE_SOURCE_LOCATION };
            UE::Tasks::FTask FinalizeRelevance;
            UE::Tasks::FTask MeshPassSetup;
            Event mMeshPassSetup;

            bool bDynamicMeshElementsPrerequisitesTriggered = false;
            bool bWaitingAllowed = false;

        } Tasks;*/
	    void LaunchVisibilityTasks(const TaskHandle* inBeginInitVisibilityPrerequisites);
        void ProcessRenderThreadTasks() override;
        void FinishGatherDynamicMeshElements() override;
        void SetupMeshPasses();//FExclusiveDepthStencil::Type BasePassDepthStencilAccess, FInstanceCullingManager& InstanceCullingManager);
        void Finish() override;
    };
    struct FInitViewTaskDatas
    {
        FInitViewTaskDatas(IVisibilityTaskData* InVisibilityTaskData)
            : mVisibilityTaskData(InVisibilityTaskData)
        {}
        IVisibilityTaskData* mVisibilityTaskData;
    };
    struct FFilterStaticMeshesForViewData
    {
        Vector3f ViewOrigin;
        int32 ForcedLODLevel;
        float LODScale;
        float MinScreenRadiusForDepthPrepassSquared;
        bool bFullEarlyZPass;

        FFilterStaticMeshesForViewData(ViewInfo& View);
    };
    struct FDrawCommandRelevancePacket
    {
        std::vector<FVisibleMeshDrawCommand*> mVisibleCachedDrawCommands[EMeshPass::Num];
        FDrawCommandRelevancePacket();
        void AddCommandsForMesh(
            int32 PrimitiveIndex,
            const PrimitiveSceneInfo* InPrimitiveSceneInfo,
            const FStaticMeshBatchRelevance& StaticMeshRelevance,
            const FStaticMeshBatch& StaticMesh,
            //EMeshDrawCommandCullingPayloadFlags CullingPayloadFlags,
            const Scene* Scene,
            bool bCanCache,
            EMeshPass::Type PassType);
    };
    struct FPrimitiveRange
    {
        int32 StartIndex;
        int32 EndIndex;
    };

    struct FDynamicPrimitive
    {
        int32 PrimitiveIndex;
        int32 ViewIndex;
        int32 StartElementIndex;
        int32 EndElementIndex;
    };

    struct FDynamicPrimitiveIndex
    {
        FDynamicPrimitiveIndex() = default;

        FDynamicPrimitiveIndex(int32 InIndex, uint8 InViewMask)
            : Index(InIndex)
            , ViewMask(InViewMask)
        {}

        uint32 Index    : 24;
        uint32 ViewMask : 8;
    };

    struct FDynamicPrimitiveIndexList
    {
        using FList = std::vector<FDynamicPrimitiveIndex>;

        bool IsEmpty() const
        {
            return Primitives.empty()
    #if WITH_EDITOR
                && EditorPrimitives.IsEmpty()
    #endif
                ;
        }

        FList Primitives;

#if WITH_EDITOR
        FList EditorPrimitives;
#endif
    };
    class FRelevancePacket
    {
    public:
        Scene*mScene;
        
        std::vector<int32> Input;
        FDrawCommandRelevancePacket mDrawCommandPacket;
        FRelevancePacket(
            FVisibilityTaskData& InTaskData,
            const ViewInfo& InView,
            int32 InViewIndex,
            //const FFilterStaticMeshesForViewData& InViewData,
            uint8* InMarkMasks,
            const FTask& PrerequisitesTask);
        void LaunchComputeRelevanceTask();
        void Finalize();
        void ComputeRelevance(FDynamicPrimitiveIndexList& DynamicPrimitiveIndexList);
    };
    class FComputeAndMarkRelevance
    {
    public:
        FVisibilityTaskData& TaskData;
        Scene*mScene;
	    ViewInfo& View;
        FTask PrerequisitesTask;
        std::vector<FRelevancePacket*> mRelevancePackets;
	    FComputeAndMarkRelevance(FVisibilityTaskData& inVisibilityTaskData, Scene* inScene, ViewInfo& inView, uint8 inViewIndex, const FTask& inPreprequisitesTask);
        void AddPrimitive(int32 Index);
        // Call when all primitives have been added.
        void Finish(const FTask& ComputeRelevanceTaskEvent);
        void Finalize();
    };
    class FVisibilityViewPacket
    {
    public:
        Scene&mScene;
        ViewInfo&mViewInfo;
	    FVisibilityTaskData& TaskData;
        struct FRelevance
        {
            FComputeAndMarkRelevance* Context = nullptr;
        } mRelevance;
	    FVisibilityViewPacket(FVisibilityTaskData& TaskData, Scene& InScene, ViewInfo& InView, int32 ViewIndex);
	    void BeginInitVisibility();
    };
    class FGPUOcclusion
    {
    public:
    };
    class FGPUOcclusionParallel final : public FGPUOcclusion
    {
    public:
    };
    class FGPUOcclusionSerial final : public FGPUOcclusion
    {
    public:
    };
}
